/**
 * #### 字符串的异步替换
 * @category util/string
 * @see https://www.bilibili.com/video/BV1yk4y1X7Rx?spm_id_from=333.788.videopod.sections&vd_source=9efadbd9bd76b9c14f9589f260a0c1ac
 * @param {*} pattern 正则表达式或字符串
 * @param {*} placement 替换函数或字符串
 * @returns Promise|string
 * @example
 * const str = "hello world";
 * const result = await str.asyncReplaceAll("o", "O");
 * console.log(result); // "hellO wOrld"
 * const result2 = await str.asyncReplaceAll(/o/g, "O");
 * console.log(result2); // "hellO wOrld"
 * const result3 = await str.asyncReplaceAll("o", (match, index, input) => {
 *   return new Promise((resolve) => {
 *     setTimeout(() => {
 *       resolve(match.toUpperCase());
 *     }, 1000);
 *     });
 *  });
 */
String.prototype.asyncReplaceAll = function (pattern, placement) {
  // 如果placement参数是字符串，则直接调用replace方法进行替换
  if (typeof placement === "string") {
    return this.replaceAll(pattern, placement);
  }
  // 如果placement参数不是字符串或函数，则抛出错误
  if (typeof placement !== "function") {
    throw new TypeError("The second parameter must be a function or a string");
  }
  let reg;
  // 如果pattern参数是字符串，则将其转换为正则表达式
  if (typeof pattern === "string") {
    // 创建一个正则表达式对象，用于匹配pattern中的所有字符
    reg = new RegExp(pattern.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"), "g");
    // 如果pattern参数是正则表达式，则判断其是否为全局正则表达式
  } else if (pattern instanceof RegExp) {
    if (!pattern.global) {
      throw new TypeError("The pattern must be a global regular expression");
    }
    reg = new RegExp(pattern);
    // 如果pattern参数既不是字符串也不是正则表达式，则抛出错误
  } else {
    throw new TypeError(
      "The first parameter must be a string or a global regular expression"
    );
  }
  let match,
    lastIndex = 0;
  const parts = [];
  // 循环匹配字符串中的所有匹配项
  while ((match = reg.exec(this)) !== null) {
    // 获取匹配项之前的字符串
    const str = this.slice(lastIndex, match.index);
    lastIndex = reg.lastIndex;
    // 获取匹配项及其相关信息
    const args = [...match, match.index, match.input];
    // 如果匹配项有分组，则将其添加到参数中
    if (match.groups) {
      args.push(match.groups);
    }
    // 调用placement函数，返回一个Promise对象
    const prom = placement(...args);
    // 将匹配项之前的字符串和Promise对象添加到parts数组中
    parts.push(str);
    parts.push(prom);
  }
  // 将匹配项之后的字符串添加到parts数组中
  parts.push(this.slice(lastIndex));
  // 返回一个Promise对象，当所有Promise对象都完成时，将结果拼接成字符串返回
  return Promise.all(parts).then((res) => {
    return res.join("");
  });
};
